package org.amse.grigory.dchess.kernel;

import java.util.ArrayList;
import org.amse.grigory.dchess.rules.Rules;

/**
 *
 * @author grigory
 */
public final class Desk {

    // Fields
    private Rules myRules;
    private Square[][] myBoard;
    private int myColor = 1;
    private ArrayList<Figure> myWhite,  myBlack;
    //fields to rollback
    private ArrayList<PreviousMove> myPreviousMove;
    private int myCurrent = -1;
    private ArrayList<Integer> myChangeX,  myChangeY;
    private ArrayList<Square> myChangeSq;

    // Methods
    public Desk(Rules rules) {
        myRules = rules.clone();
        // will be used later
        myWhite = new ArrayList<Figure>();
        myBlack = new ArrayList<Figure>();
        myPreviousMove = new ArrayList<PreviousMove>();
        myChangeX = new ArrayList<Integer>();
        myChangeY = new ArrayList<Integer>();
        myChangeSq = new ArrayList<Square>();

        //init
        int width = myRules.getWidth();
        int height = myRules.getHeight();
        myBoard = new Square[width][height];
        myColor = 1;
        myRules.start(myBoard);
    }

    //
    @Override
    public String toString() {
        String temp = "";
        temp += "Name=" + myRules.getName() + "\n";
        temp += "Height=" + myRules.getHeight() + "\n";
        temp += "Width=" + myRules.getWidth() + "\n";
        if (myBoard != null) {
            String c[][] = new String[myRules.getWidth()][myRules.getHeight()];
            for (int i = 0; i < myRules.getWidth(); ++i) {
                for (int j = 0; j < myRules.getHeight(); ++j) {
                    if (myBoard[i][j].getFigure() == null) {
                        c[i][j] = "|---|";
                    } else {
                        c[i][j] = "|";
                        if (myBoard[i][j].getColor() == -1) {
                            c[i][j] += "b";
                        } else {
                            c[i][j] += "w";
                        }
                        c[i][j] += myBoard[i][j].getFigure().getName().substring(0, 2);
                        c[i][j] += "|";
                    }

                }
            }
            for (int i = 0; i < myRules.getIllegalSquareNumber(); ++i) {
                int x = myRules.getIllegalX(i);
                int y = myRules.getIllegalY(i);
                c[x][y] = "     ";
            }

            temp += "\n  ";
            for (int i = 0; i < myRules.getWidth(); ++i) {
                temp += "  " + (i + 1) + "  ";
            }
            temp += "\n";
            for (int i = 0; i < myRules.getHeight(); ++i) {
                temp += (i + 1) + " ";
                for (int j = 0; j < myRules.getWidth(); ++j) {
                    temp += c[j][i];
                }
                temp += "\n";
            }
        }


        return temp;
    }
    // Getters

    public int getHeight() {
        return myRules.getHeight();
    }

    public int getWidth() {
        return myRules.getWidth();
    }

    public String getName() {
        return myRules.getName();
    }

    public int getIllegalSquareNumber() {
        return myRules.getIllegalSquareNumber();
    }

    public int getIllegalX(int index) {
        return myRules.getIllegalX(index);
    }

    public int getIllegalY(int index) {
        return myRules.getIllegalY(index);
    }

    public String getFigureName(int x, int y) {
        if (myBoard[x][y].getFigure() == null) {
            return null;
        }
        return myBoard[x][y].getFigure().getName();
    }

    public int getFigureColor(int x, int y) {
        return myBoard[x][y].getColor();
    }

    public int getColor() {
        return myColor;
    }

    public String getMessage() {
        return myRules.getMessage(myBoard, myColor);
    }

    public boolean undo() {
        if (myCurrent == -1) {
            return false;
        }
        PreviousMove pmove = myPreviousMove.get(myCurrent);
        int n = pmove.getCount();
        for (int i = 0; i < n; ++i) {
            myBoard[pmove.getX(i)][pmove.getY(i)] = pmove.getSquare(i).clone();
        }


        myCurrent--;
        myColor = -myColor;
        checkFigures();
        return true;
    }

    public boolean redo() {
        if (canRedo()) {
            PreviousMove nmove = myPreviousMove.get(myCurrent + 1);
            simpleMakeMove(nmove.getX(0) + 1, nmove.getY(0) + 1, nmove.getX(1) + 1, nmove.getY(1) + 1);
            myCurrent++;
            checkFigures();
            return true;
        }
        return false;
    }

    // Methods
    public void start() {
        int width = myRules.getWidth();
        int height = myRules.getHeight();
        myBoard = new Square[width][height];
        myColor = 1;
        myRules.start(myBoard);
        myPreviousMove = new ArrayList<PreviousMove>();
        checkFigures();
    }

    public boolean canMove(int x, int y, int tx, int ty) {
        return myRules.canMove(myBoard, x, y, tx, ty, myColor);
    }

    public boolean makeMove(int x, int y, int tx, int ty) {
        if (myRules.canMove(myBoard, x, y, tx, ty, myColor)) {
            if (myCurrent + 1 < myPreviousMove.size()) {
                myPreviousMove.subList(myCurrent + 1, myPreviousMove.size()).clear();
            }
            if (myRules.makeMove(myBoard, x, y, tx, ty, myColor, this)) {
                myColor = -myColor;
                checkFigures();                
            }             
            PreviousMove pmove = new PreviousMove(myChangeSq, myChangeX, myChangeY);

            myPreviousMove.add(pmove);
            myCurrent++;
            return true;
        }
        return false;

    }

    private boolean simpleMakeMove(int x, int y, int tx, int ty) {
        if (myRules.makeSMove(myBoard, x, y, tx, ty, myColor)) {
            myColor = -myColor;
            checkFigures();
            return true;
        } else {
            return false;
        }
    }
    //TODO

    public int isWin() {
        return myRules.isWin(myBoard, myColor);
    }

    @Override
    public Desk clone() {
        Desk temp = new Desk(myRules);
        temp.myColor = myColor;
        int w = myRules.getWidth();
        int h = myRules.getHeight();
        temp.myBoard = new Square[w][h];
        for (int i = 0; i < w; i++) {
            for (int j = 0; j < h; j++) {
                temp.myBoard[i][j] = myBoard[i][j].clone();
            }
        }
        temp.myWhite = new ArrayList<Figure>(myWhite.size());
        temp.myBlack = new ArrayList<Figure>(myBlack.size());
        for (int i = 0; i < myWhite.size(); i++) {
            temp.myWhite.add(myWhite.get(i));
        }
        for (int i = 0; i < myBlack.size(); i++) {
            temp.myBlack.add(myBlack.get(i));
        }
        temp.myPreviousMove = new ArrayList<PreviousMove>();
        for (int i = 0; i < myPreviousMove.size(); i++) {
            temp.myPreviousMove.add(myPreviousMove.get(i).clone());
        }
        return temp;
    }

    public void checkFigures() {
        myWhite = new ArrayList<Figure>();
        myBlack = new ArrayList<Figure>();
        for (int i = 0; i < myRules.getWidth(); ++i) {
            for (int j = 0; j < myRules.getHeight(); ++j) {
                if (myBoard[i][j].getFigure() != null) {
                    if (myBoard[i][j].getFigure().getColor() == Figure.COLOR_WHITE) {
                        myBoard[i][j].getFigure().setX(i + 1);
                        myBoard[i][j].getFigure().setY(j + 1);
                        myBoard[i][j].getFigure().setColor(Figure.COLOR_WHITE);
                        myWhite.add(myBoard[i][j].getFigure());
                    } else {
                        myBoard[i][j].getFigure().setX(i + 1);
                        myBoard[i][j].getFigure().setY(j + 1);
                        myBoard[i][j].getFigure().setColor(Figure.COLOR_BLACK);
                        myBlack.add(myBoard[i][j].getFigure());
                    }
                }
            }
        }

    }

    public Figure getWhiteFigure(int index) {
        return myWhite.get(index);
    }

    public Figure getBlackFigure(int index) {
        return myBlack.get(index);
    }

    public int getWhiteCount() {
        return myWhite.size();
    }

    public int getBlackCount() {
        return myBlack.size();
    }

    public boolean canUndo() {
        if (myPreviousMove == null) {
            return false;
        }
        return myCurrent != -1;
    }

    public boolean canRedo() {
        if (myPreviousMove == null) {
            return false;
        }
        if (myPreviousMove.size() == 0) {
            return false;
        }
        return (myPreviousMove.size() - 1) != myCurrent;
    }

    public Move getMove(int index) {
        if (index < 0) {
            return null;
        }
        if (index >= myPreviousMove.size()) {
            return null;
        }
        PreviousMove move = myPreviousMove.get(index);
        int fx = move.getX(0);
        int fy = move.getY(0);
        int tx = move.getX(1);
        int ty = move.getY(1);
        return new Move(fx, fy, tx, ty);
    }

    public int getCurrentMoveNumber() {
        return myCurrent;
    }

    public int getMoveCount() {
        return myPreviousMove.size();
    }

    public void newChange() {
        myChangeSq = new ArrayList<Square>();
        myChangeX = new ArrayList<Integer>();
        myChangeY = new ArrayList<Integer>();
    }

    public void addChange(Square square, int x, int y) {
        myChangeSq.add(square.clone());
        myChangeX.add(x);
        myChangeY.add(y);
    }
}
